using System;
namespace ch.ethz.ssh2.crypto.cipher
{
	
	/// <summary> CBCMode.
	/// 
	/// </summary>
	/// <author>  Christian Plattner
	/// </author>
	/// <version>  2.50, 03/15/10
	/// </version>
	public class CBCMode : BlockCipher
	{
		virtual public int BlockSize
		{
			get
			{
				return blockSize;
			}
			
		}
		internal BlockCipher tc;
		internal int blockSize;
		internal bool doEncrypt;
		
		internal byte[] cbc_vector;
		internal byte[] tmp_vector;
		
		public virtual void  init(bool forEncryption, byte[] key)
		{
		}
		
		public CBCMode(BlockCipher tc, byte[] iv, bool doEncrypt)
		{
			this.tc = tc;
			this.blockSize = tc.BlockSize;
			this.doEncrypt = doEncrypt;
			
			if (this.blockSize != iv.Length)
				throw new System.ArgumentException("IV must be " + blockSize + " bytes long! (currently " + iv.Length + ")");
			
			this.cbc_vector = new byte[blockSize];
			this.tmp_vector = new byte[blockSize];
			Array.Copy(iv, 0, cbc_vector, 0, blockSize);
		}
		
		private void  encryptBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
		{
			for (int i = 0; i < blockSize; i++)
				cbc_vector[i] ^= src[srcoff + i];
			
			tc.transformBlock(cbc_vector, 0, dst, dstoff);
			
			Array.Copy(dst, dstoff, cbc_vector, 0, blockSize);
		}
		
		private void  decryptBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
		{
			/* Assume the worst, src and dst are overlapping... */
			
			Array.Copy(src, srcoff, tmp_vector, 0, blockSize);
			
			tc.transformBlock(src, srcoff, dst, dstoff);
			
			for (int i = 0; i < blockSize; i++)
				dst[dstoff + i] ^= cbc_vector[i];
			
			/* ...that is why we need a tmp buffer. */
			
			byte[] swap = cbc_vector;
			cbc_vector = tmp_vector;
			tmp_vector = swap;
		}
		
		public virtual void  transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
		{
			if (doEncrypt)
				encryptBlock(src, srcoff, dst, dstoff);
			else
				decryptBlock(src, srcoff, dst, dstoff);
		}
	}
}